package com.foxit.pdf2html5.mongodb;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

import org.springframework.beans.factory.DisposableBean;
import org.springframework.beans.factory.FactoryBean;
import org.springframework.beans.factory.InitializingBean;

import com.mongodb.Mongo;
import com.mongodb.MongoClient;
import com.mongodb.MongoClientOptions;
import com.mongodb.MongoCredential;
import com.mongodb.ReadPreference;
import com.mongodb.ServerAddress;
import com.mongodb.WriteConcern;

public class MongoFactoryBean implements FactoryBean<Mongo>, InitializingBean, DisposableBean{

	private MongoClient  mongo = null;
	// mongoDB配置对象
	private MongoClientOptions mongoOptions = null;
	// 设定写策略(出错时是否抛异常)，默认采用SAFE模式(需要抛异常)
	private WriteConcern writeConcern = WriteConcern.SAFE;
	// 表示服务器列表(主从复制或者分片)的字符串数组
	private String serverStrings;
	private String password = "";
	private String userName = "";
	private String dataBaseName = "";
	
	public String getDataBaseName() {
		return dataBaseName;
	}

	public void setDataBaseName(String dataBaseName) {
		this.dataBaseName = dataBaseName;
	}

	// 是否主从分离(读取从库)，默认读写都在主库
	private boolean readSecondary = false;
	
	private int connectionsPerHost = 100;
	private int threadsAllowedToBlockForConnectionMultiplier = 5;
	private int maxWaitTime = 1000 * 60 * 2;
	private int maxConnectionIdleTime;
	private int maxConnectionLifeTime;
	
	public String getPassword() {
		return password;
	}

	public void setPassword(String password) {
		this.password = password;
	}

	public String getUserName() {
		return userName;
	}

	public void setUserName(String userName) {
		this.userName = userName;
	}

	private int connectTimeout = 1000 * 10;
	private int socketTimeout = 0;
	private boolean socketKeepAlive = false;
	private boolean autoConnectRetry = false;
	private long maxAutoConnectRetryTime = 0;
	private boolean cursorFinalizerEnabled = true;
	
	public String getServerStrings() {
		return serverStrings;
	}

	public void setServerStrings(String serverStrings) {
		this.serverStrings = serverStrings;
	}

	public boolean isReadSecondary() {
		return readSecondary;
	}

	public void setReadSecondary(boolean readSecondary) {
		this.readSecondary = readSecondary;
	}

	public int getConnectionsPerHost() {
		return connectionsPerHost;
	}

	public void setConnectionsPerHost(int connectionsPerHost) {
		this.connectionsPerHost = connectionsPerHost;
	}

	public int getThreadsAllowedToBlockForConnectionMultiplier() {
		return threadsAllowedToBlockForConnectionMultiplier;
	}

	public void setThreadsAllowedToBlockForConnectionMultiplier(
			int threadsAllowedToBlockForConnectionMultiplier) {
		this.threadsAllowedToBlockForConnectionMultiplier = threadsAllowedToBlockForConnectionMultiplier;
	}

	public int getMaxWaitTime() {
		return maxWaitTime;
	}

	public void setMaxWaitTime(int maxWaitTime) {
		this.maxWaitTime = maxWaitTime;
	}

	public int getMaxConnectionIdleTime() {
		return maxConnectionIdleTime;
	}

	public void setMaxConnectionIdleTime(int maxConnectionIdleTime) {
		this.maxConnectionIdleTime = maxConnectionIdleTime;
	}

	public int getMaxConnectionLifeTime() {
		return maxConnectionLifeTime;
	}

	public void setMaxConnectionLifeTime(int maxConnectionLifeTime) {
		this.maxConnectionLifeTime = maxConnectionLifeTime;
	}

	public int getConnectTimeout() {
		return connectTimeout;
	}

	public void setConnectTimeout(int connectTimeout) {
		this.connectTimeout = connectTimeout;
	}

	public int getSocketTimeout() {
		return socketTimeout;
	}

	public void setSocketTimeout(int socketTimeout) {
		this.socketTimeout = socketTimeout;
	}

	public boolean isSocketKeepAlive() {
		return socketKeepAlive;
	}

	public void setSocketKeepAlive(boolean socketKeepAlive) {
		this.socketKeepAlive = socketKeepAlive;
	}

	public boolean isAutoConnectRetry() {
		return autoConnectRetry;
	}

	public void setAutoConnectRetry(boolean autoConnectRetry) {
		this.autoConnectRetry = autoConnectRetry;
	}

	public long getMaxAutoConnectRetryTime() {
		return maxAutoConnectRetryTime;
	}

	public void setMaxAutoConnectRetryTime(long maxAutoConnectRetryTime) {
		this.maxAutoConnectRetryTime = maxAutoConnectRetryTime;
	}

	public boolean isCursorFinalizerEnabled() {
		return cursorFinalizerEnabled;
	}

	public void setCursorFinalizerEnabled(boolean cursorFinalizerEnabled) {
		this.cursorFinalizerEnabled = cursorFinalizerEnabled;
	}

	public WriteConcern getWriteConcern() {
		return writeConcern;
	}

	/**
	 * Sets the {@link WriteConcern} to be configured for the {@link Mongo} instance to be created.
	 * 
	 * @param writeConcern
	 */
	public void setWriteConcern(WriteConcern writeConcern) {
		this.writeConcern = writeConcern;
	}
	
	public void destroy() throws Exception {
		this.mongo.close();	
	}
	
	private List<ServerAddress> getServerList() throws Exception {
		List<ServerAddress> serverList = new ArrayList<ServerAddress>();
		String[] serverStringList = serverStrings.split(",");
		try {
			for (String serverString : serverStringList) {
				String[] temp = serverString.split(":");
				String host = temp[0];
				if (temp.length > 2) {
					throw new IllegalArgumentException(
							"Invalid server address string: " + serverString);
				}
				if (temp.length == 2) {
					serverList.add(new ServerAddress(host, Integer
							.parseInt(temp[1])));
				} else {
					serverList.add(new ServerAddress(host));
				}
			}
			return serverList;
		} catch (Exception e) {
			throw new Exception("Error while converting serverString to ServerAddressList",e);
		}
	}

	public void afterPropertiesSet() throws Exception {
		MongoClient  mongo = null;
		List<ServerAddress> serverList = getServerList();
		MongoCredential credential = MongoCredential.createMongoCRCredential
				(this.userName, this.dataBaseName, this.password.toCharArray());

		MongoClientOptions.Builder builder = new MongoClientOptions.Builder();
		builder.autoConnectRetry(this.autoConnectRetry);
		builder.maxAutoConnectRetryTime(maxAutoConnectRetryTime);
		builder.connectionsPerHost(connectionsPerHost);
		builder.connectTimeout(connectTimeout);
		builder.cursorFinalizerEnabled(cursorFinalizerEnabled);
		builder.maxWaitTime(maxWaitTime);
		builder.threadsAllowedToBlockForConnectionMultiplier(threadsAllowedToBlockForConnectionMultiplier);
		builder.socketTimeout(socketTimeout);
		builder.socketKeepAlive(socketKeepAlive);
		this.mongoOptions = builder.build();
		
		if (serverList.size() == 0) {
			mongo = new MongoClient();
		} else if (serverList.size() == 1) {
			if (mongoOptions != null) {
				mongo = new MongoClient (serverList.get(0), mongoOptions);
			} else {
				mongo = new MongoClient (serverList.get(0));
			}
		} else {
			if (mongoOptions != null) {
				mongo = new MongoClient (serverList,mongoOptions);
			} else {
				mongo = new MongoClient (serverList);
			}
		}

		if (writeConcern != null) {
			mongo.setWriteConcern(writeConcern);
		}
		// 设定主从分离
		if (this.readSecondary) {
			mongo.setReadPreference(ReadPreference.secondaryPreferred());
		}
		

		this.mongo = mongo;
	}

	public Mongo getObject() throws Exception {
		return this.mongo;
	}

	public Class<?> getObjectType() {
		return Mongo.class;
	}

	public boolean isSingleton() {
		return true;
	}

}
